package com.hk.hkvedio.service.impl;

import com.hk.hkvedio.config.HKConfig;
import com.hk.hkvedio.service.IHkVideoService;
import com.hk.hkvedio.utils.HCNetSDK;
import com.hk.hkvedio.vo.FileInfoVo;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.NativeLongByReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class HkVideoServiceImpl implements IHkVideoService {

    private Logger logger = LoggerFactory.getLogger(HkVideoServiceImpl.class);

    //登录返回
    private NativeLong nlUserID;

    private HCNetSDK hkSdk = HCNetSDK.INSTANCE;

    private HCNetSDK.NET_DVR_DEVICEINFO_V30 m_strDeviceInfo;

    private HCNetSDK.NET_DVR_IPPARACFG m_strIpparaCfg;

    private HCNetSDK.NET_DVR_USER_LOGIN_INFO struLoginInfo;
    private HCNetSDK.NET_DVR_DEVICEINFO_V40 struDeviceInfo;

    //线程安全时间格式化
    private ThreadLocal<DateFormat> yyyyMMdd = new ThreadLocal<DateFormat>() {
        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("yyyyMMdd");
        }
    };
    //线程安全时间格式化
    private ThreadLocal<DateFormat> HHmmss = new ThreadLocal<DateFormat>() {
        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("HHmmss");
        }
    };
    //线程安全时间格式化
    private ThreadLocal<DateFormat> dateTime = new ThreadLocal<DateFormat>() {
        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("yyyy/MM/ddHH:mm:ss");
        }
    };

    @Override
    public HCNetSDK getHkSdk() {
        return hkSdk;
    }

    @Override
    public HCNetSDK net_dvr_init() {
        boolean b = hkSdk.NET_DVR_Init();
        if (b) {
            //设置连接时间与重连时间
            hkSdk.NET_DVR_SetConnectTime(2000, 1);
            hkSdk.NET_DVR_SetReconnect(10000, true);
        }
        logger.info("海康sdk初始化,{}", (b ? "成功-" : "失败-") + b);
        return hkSdk;
    }

    @Override
    public NativeLong net_dvr_login_v30() {
        nlUserID = hkSdk.NET_DVR_Login_V30(HKConfig.ip, HKConfig.port, HKConfig.username, HKConfig.password, m_strDeviceInfo);
        logger.info("设备注册{}", nlUserID.longValue() == -1 ? "失败" : "成功");
        if (nlUserID.longValue() == -1) {
            logger.info("错误码：{}", hkSdk.NET_DVR_GetLastError());
        }
        logger.info("登录返回值：{}", nlUserID.longValue());
        logger.info("设备注册{}", nlUserID.longValue() == -1 ? "失败" : "成功");
        logger.info("模拟通道个数 {}", m_strDeviceInfo.byChanNum);
        logger.info("通道起始通道号 {}", m_strDeviceInfo.byStartChan);
        logger.info("最大数字通道个数 {}", m_strDeviceInfo.byIPChanNum);
        logger.info("设备序列号 {}", new String(m_strDeviceInfo.sSerialNumber).trim());
        return nlUserID;
    }

    @Override
    public NativeLong net_dvr_login_v40() {
        struLoginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO();
        struDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();
        Pointer deviceInfoV40 = struDeviceInfo.getPointer();
        Pointer loginInfo = struLoginInfo.getPointer();

        struLoginInfo.sDeviceAddress = HKConfig.ip.getBytes();
        struLoginInfo.sPassword = HKConfig.password.getBytes();
        struLoginInfo.sUserName = HKConfig.username.getBytes();
        struLoginInfo.wPort = HKConfig.port;
        struLoginInfo.write();
        //设备注册
        nlUserID = hkSdk.NET_DVR_Login_V40(loginInfo, deviceInfoV40);
        logger.info("设备注册{}", nlUserID.longValue() == -1 ? "失败" : "成功");
        if (nlUserID.longValue() == -1) {
            logger.info("错误码：{}", hkSdk.NET_DVR_GetLastError());
        }
        struDeviceInfo.read();
        return nlUserID;
    }

    @Override
    public List<Integer> createDeviceTreeV30() {
        ArrayList<Integer> list = new ArrayList<>();
        IntByReference ibrBytesReturned = new IntByReference(0);//获取IP接入配置参数
        boolean bRet = false;

        m_strIpparaCfg = new HCNetSDK.NET_DVR_IPPARACFG();
        m_strIpparaCfg.write();
        Pointer lpIpParaConfig = m_strIpparaCfg.getPointer();
        bRet = hkSdk.NET_DVR_GetDVRConfig(nlUserID, HCNetSDK.NET_DVR_GET_IPPARACFG, new NativeLong(0), lpIpParaConfig, m_strIpparaCfg.size(), ibrBytesReturned);
        m_strIpparaCfg.read();
        if (!bRet) {
            logger.info("设备不支持,则表示没有IP通道");
            //设备不支持,则表示没有IP通道
            for (int iChannum = 0; iChannum < m_strDeviceInfo.byChanNum; iChannum++) {
                list.add(iChannum + m_strDeviceInfo.byStartChan);
            }
        } else {
            logger.info("设备支持IP通道");
            //设备支持IP通道
            for (int iChannum = 0; iChannum < m_strDeviceInfo.byChanNum; iChannum++) {
                if (m_strIpparaCfg.byAnalogChanEnable[iChannum] == 1) {
                    list.add(iChannum + m_strDeviceInfo.byStartChan);
                }
            }
            for (int iChannum = 0; iChannum < HCNetSDK.MAX_IP_CHANNEL; iChannum++) {
                if (m_strIpparaCfg.struIPChanInfo[iChannum].byEnable == 1) {
                    int channelNumber = iChannum + m_strDeviceInfo.byStartChan;
                    logger.info("原始模拟通道号：{}", channelNumber);
                    logger.info("原始模拟通道号：{}", channelNumber + 32);
                    list.add(channelNumber + 32);
                }
            }
        }
        return list;
    }

    @Override
    public LinkedList<Integer> createDeviceTreeV40() {
        LinkedList<Integer> channelNo = new LinkedList<>();
        HCNetSDK.NET_DVR_DEVICEINFO_V30 strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V30();
        strDeviceInfo = struDeviceInfo.struDeviceV30;
        IntByReference ibrBytesReturned = new IntByReference(0);// get ip param
        HCNetSDK.NET_DVR_IPPARACFG strIpparaCfg = new HCNetSDK.NET_DVR_IPPARACFG();
        Pointer lpIpParaConfig = strIpparaCfg.getPointer();
        boolean bRet = hkSdk.NET_DVR_GetDVRConfig(nlUserID, HCNetSDK.NET_DVR_GET_IPPARACFG, new NativeLong(0), lpIpParaConfig,
                strIpparaCfg.size(), ibrBytesReturned);
        logger.info("获取设备信息：{}", bRet);
        strIpparaCfg.read();
        if (!bRet) {
            // device not support,has no ip camera
            for (int iChannum = 0; iChannum < strDeviceInfo.byChanNum; iChannum++) {
                logger.info("通道号：{}", iChannum + strDeviceInfo.byStartChan + 32);
                channelNo.add(iChannum + strDeviceInfo.byStartChan + 32);
            }
        } else {
            // ip camera
            for (int iChannum = 0; iChannum < strDeviceInfo.byChanNum; iChannum++) {
                {
                    logger.info("通道号：{}", iChannum + strDeviceInfo.byStartChan + 32);
                    channelNo.add(iChannum + strDeviceInfo.byStartChan + 32);
                }
            }
            for (int iChannum = 0; iChannum < strDeviceInfo.byIPChanNum; iChannum++) {
                logger.info("通道号：{}", iChannum + strDeviceInfo.byStartChan + 32);
                channelNo.add(iChannum + strDeviceInfo.byStartChan + 32);
            }
        }
        return channelNo;
    }

    @Override
    public String getChnanelName(Integer chnanelNumber) {
        //获取通道名称，通道名称在图片参数里面，所以实体对象使用图片参数的
        IntByReference ibr = new IntByReference(0);
        HCNetSDK.NET_DVR_PICCFG_V30 m_struPicCfg = new HCNetSDK.NET_DVR_PICCFG_V30();
        m_struPicCfg.write();
        Pointer lpPicConfig = m_struPicCfg.getPointer();
        boolean getDVRConfigSuc = hkSdk.NET_DVR_GetDVRConfig(nlUserID, HCNetSDK.NET_DVR_GET_PICCFG_V30,
                new NativeLong(chnanelNumber), lpPicConfig, m_struPicCfg.size(), ibr);
        m_struPicCfg.read();
        if (!getDVRConfigSuc) {
            logger.info("获取通道名称 error code {}", hkSdk.NET_DVR_GetLastError());
        }
        //显示参数
        String[] sName = new String[2];
        sName = new String(m_struPicCfg.sChanName, StandardCharsets.UTF_8).split("\0", 2);
        String chnanelName = sName[0];
        System.out.println("通道名称：" + chnanelName);
        return chnanelName;
    }

    @Override
    public List<FileInfoVo> findFileV30(HCNetSDK.NET_DVR_TIME startTime, HCNetSDK.NET_DVR_TIME endTime, Integer channelNumber, Integer fileType) throws Exception {
        HCNetSDK.NET_DVR_FILECOND fileCond = new HCNetSDK.NET_DVR_FILECOND();
        fileCond.lChannel = new NativeLong(channelNumber);//通道号
        fileCond.dwFileType = 0xff;
        if (!Objects.equals(fileType, null)) {
            //文件类型
            fileCond.dwFileType = fileType;
        }
        //fileCond.dwFileType = fileType;//录象文件类型0xff－全部，0－定时录像,1-移动侦测 ，2－报警触发，3-报警|移动侦测 4-报警&移动侦测 5-命令触发 6-手动录像
        fileCond.dwIsLocked = 0xff;//是否锁定 0-正常文件,1-锁定文件, 0xff表示所有文件
        //fileCond.dwUseCardNo = 0;//是否使用卡号，1使用，0不使用
        fileCond.struStartTime = startTime;//开始时间
        fileCond.struStopTime = endTime;//结束时间

        NativeLong fileV30 = hkSdk.NET_DVR_FindFile_V30(nlUserID, fileCond);
        long findFile = fileV30.longValue();
        if (findFile == -1) {
            logger.info("错误码：{}", hkSdk.NET_DVR_GetLastError());
            NativeLongByReference ss = new NativeLongByReference();
            ss.setValue(fileV30);
            logger.info("错误码：{}", hkSdk.NET_DVR_GetLastError());
            logger.info("错误信息：{}", hkSdk.NET_DVR_GetErrorMsg(ss));
        }
        List<FileInfoVo> list = listFileInfoV30(fileV30, new ArrayList<FileInfoVo>());
        if (!list.isEmpty()) {
            //获取通道名称
            String chnanelName = getChnanelName(channelNumber);
            list.forEach(l -> {
                l.setDeviceId(chnanelName);
            });
        }


        logger.info("文件数量：{}", list.size());
        return list;
    }

    @Override
    public List<FileInfoVo> findFileV40(HCNetSDK.NET_DVR_TIME startTime, HCNetSDK.NET_DVR_TIME endTime, Integer channelNumber, Integer fileType) throws Exception {
        HCNetSDK.NET_DVR_FILECOND_V40 strFilecond = new HCNetSDK.NET_DVR_FILECOND_V40();
        strFilecond.struStartTime = startTime;
        strFilecond.struStopTime = endTime;
        //通道号
        strFilecond.lChannel = new NativeLong(channelNumber);
        strFilecond.dwFileType = 0xff;
        if (!Objects.equals(fileType, null)) {
            //文件类型
            strFilecond.dwFileType = fileType;
        }
        //是否锁定 0-正常文件,1-锁定文件, 0xff表示所有文件
        strFilecond.dwIsLocked = 0xff;
        NativeLong lFindFile = hkSdk.NET_DVR_FindFile_V40(nlUserID, strFilecond);
        long findFile = lFindFile.longValue();
        if (findFile == -1) {
            logger.info("错误码：{}", hkSdk.NET_DVR_GetLastError());
            NativeLongByReference ss = new NativeLongByReference();
            ss.setValue(lFindFile);
            logger.info("错误码：{}", hkSdk.NET_DVR_GetLastError());
            logger.info("错误信息：{}", hkSdk.NET_DVR_GetErrorMsg(ss));
        }
        List<FileInfoVo> list = listFileInfoV40(lFindFile, new ArrayList<FileInfoVo>());
        if (!list.isEmpty()) {
            //获取通道名称
            String chnanelName = getChnanelName(channelNumber);
            list.forEach(l -> {
                l.setDeviceId(chnanelName);
            });
        }
        return list;
    }

    /**
     * 递归获取文件
     *
     * @param nativeLong
     * @param list
     * @return
     */
    public List<FileInfoVo> listFileInfoV30(NativeLong nativeLong, List<FileInfoVo> list) throws Exception {
        HCNetSDK.NET_DVR_FINDDATA_V30 strFile = new HCNetSDK.NET_DVR_FINDDATA_V30();
        NativeLong lnext = hkSdk.NET_DVR_FindNextFile_V30(nativeLong, strFile);
        if (lnext.longValue() == HCNetSDK.NET_DVR_FILE_SUCCESS) {

            String beginTime = strFile.struStartTime.toStringTime();
            Date date = dateTime.get().parse(beginTime);

            //搜索成功
            logger.info("搜索到文件");
            FileInfoVo vo = new FileInfoVo();
            vo.setFileName(new String(strFile.sFileName).trim());
            vo.setFileSize(strFile.dwFileSize);
            vo.setFileBeginTime(beginTime);
            vo.setFileEndTime(strFile.struStopTime.toStringTime());
            vo.setHhmmss(HHmmss.get().format(date));
            vo.setYyyyMMdd(yyyyMMdd.get().format(date));

            //判断是否需要下载这个文件,当文件大小低于当前值不下载
            if (HKConfig.fileMinSize.compareTo(vo.getFileSize() / (1024 * 1024)) == -1) {
                list.add(vo);
            }
            logger.info("文件大小：{}", strFile.dwFileSize / (1024 * 1024) + " mb");
            logger.info("文件名称：{}", vo.getFileName());
            logger.info("文件开始时间：{}", beginTime);
            logger.info("文件结束时间：{}", strFile.struStopTime.toStringTime());
            return listFileInfoV30(nativeLong, list);
        } else {
            if (lnext.longValue() == HCNetSDK.NET_DVR_ISFINDING) {
                //搜索中
                logger.info("搜索中");
                return listFileInfoV30(nativeLong, list);
            } else {
                if (lnext.longValue() == HCNetSDK.NET_DVR_FILE_NOFIND) {
                    logger.info("没有搜到文件");
                    //return listFileInfo(fileV30, list);
                } else {
                    logger.info("搜索文件结束");
                    boolean flag = hkSdk.NET_DVR_FindClose_V30(nativeLong);
                    if (!flag) {
                        logger.info("结束搜索-失败");
                    }
                }
            }
        }
        return list;
    }

    /**
     * 递归获取文件
     */
    public List<FileInfoVo> listFileInfoV40(NativeLong nativeLong, List<FileInfoVo> list) {
        try {
            HCNetSDK.NET_DVR_FINDDATA_V40 strFile = new HCNetSDK.NET_DVR_FINDDATA_V40();
            NativeLong lnext = hkSdk.NET_DVR_FindNextFile_V40(nativeLong, strFile);
            if (lnext.longValue() == HCNetSDK.NET_DVR_FILE_SUCCESS) {
                String beginTime = strFile.struStartTime.toStringTime();
                Date date = dateTime.get().parse(beginTime);

                //搜索成功
                logger.info("搜索到文件");
                FileInfoVo vo = new FileInfoVo();
                vo.setFileName(new String(strFile.sFileName).trim());
                vo.setFileSize(strFile.dwFileSize);
                vo.setFileBeginTime(beginTime);
                vo.setFileEndTime(strFile.struStopTime.toStringTime());
                vo.setHhmmss(HHmmss.get().format(date));
                vo.setYyyyMMdd(yyyyMMdd.get().format(date));

                //判断是否需要下载这个文件,当文件大小低于当前值不下载
                if (HKConfig.fileMinSize.compareTo(vo.getFileSize() / (1024 * 1024)) == -1) {
                    list.add(vo);
                }
                logger.info("文件大小：{}", strFile.dwFileSize / (1024 * 1024) + " mb");
                logger.info("文件名称：{}", vo.getFileName());
                logger.info("文件开始时间：{}", beginTime);
                logger.info("文件结束时间：{}", strFile.struStopTime.toStringTime());
                return listFileInfoV30(nativeLong, list);
            } else {
                if (lnext.longValue() == HCNetSDK.NET_DVR_ISFINDING) {
                    //搜索中
                    logger.info("搜索中");
                    return listFileInfoV30(nativeLong, list);
                } else {
                    if (lnext.longValue() == HCNetSDK.NET_DVR_FILE_NOFIND) {
                        logger.info("没有搜到文件");
                        //return listFileInfo(fileV30, list);
                    } else {
                        logger.info("搜索文件结束");
                        boolean flag = hkSdk.NET_DVR_FindClose_V30(nativeLong);
                        if (!flag) {
                            logger.info("结束搜索-失败");
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }


}
